\subsection{MIPS}

\subsubsection{О \q{глобальном указателе} (\q{global pointer})}
\label{MIPS_GP}

\myindex{MIPS!\GlobalPointer}
\q{Глобальный указатель} (\q{global pointer})~--- это важная концепция в MIPS.
Как мы уже возможно знаем, каждая инструкция в MIPS имеет размер 32 бита, поэтому невозможно
закодировать 32-битный адрес внутри одной инструкции. Вместо этого нужно использовать пару инструкций
(как это сделал GCC для загрузки адреса текстовой строки в нашем примере).
С другой стороны, используя только одну инструкцию, 
возможно загружать данные по адресам в пределах $register-32768...register+32767$, потому что 16 бит
знакового смещения можно закодировать в одной инструкции).
Так мы можем выделить какой-то регистр для этих целей и ещё выделить буфер в 64KiB для самых 
часто используемых данных.
Выделенный регистр называется \q{глобальный указатель} (\q{global pointer}) и он указывает на середину
области 64KiB.
Эта область обычно содержит глобальные переменные и адреса импортированных функций вроде \printf,
потому что разработчики GCC решили, что получение адреса функции должно быть как можно более быстрой операцией,
исполняющейся за одну инструкцию вместо двух.
В ELF-файле эта 64KiB-область находится частично в секции .sbss (\q{small \ac{BSS}}) для неинициализированных
данных и в секции .sdata (\q{small data}) для инициализированных данных.
Это значит что программист может выбирать, к чему нужен как можно более быстрый доступ, и затем расположить
это в секциях .sdata/.sbss.
Некоторые программисты \q{старой школы} могут вспомнить модель памяти в MS-DOS \myref{8086_memory_model} 
или в менеджерах памяти вроде XMS/EMS, где вся память делилась на блоки по 64KiB.

\myindex{PowerPC}
Эта концепция применяется не только в MIPS. По крайней мере PowerPC также использует эту технику.

\subsubsection{\Optimizing GCC}

Рассмотрим следующий пример, иллюстрирующий концепцию \q{глобального указателя}.

\lstinputlisting[caption=\Optimizing GCC 4.4.5 (\assemblyOutput),numbers=left,style=customasmMIPS]{patterns/01_helloworld/MIPS/hw_O3_RU.s}

Как видно, регистр \$GP в прологе функции выставляется в середину этой области.
Регистр \ac{RA} сохраняется в локальном стеке.
Здесь также используется \puts вместо \printf.
\myindex{MIPS!\Instructions!LW}
Адрес функции \puts загружается в \$25 инструкцией \INS{LW} (\q{Load Word}).
\myindex{MIPS!\Instructions!LUI}
\myindex{MIPS!\Instructions!ADDIU}
Затем адрес текстовой строки загружается в \$4 парой инструкций \INS{LUI} (\q{Load Upper Immediate}) и
\INS{ADDIU} (\q{Add Immediate Unsigned Word}).
\INS{LUI} устанавливает старшие 16 бит регистра (поэтому в имени инструкции присутствует \q{upper}) и \INS{ADDIU}
прибавляет младшие 16 бит к адресу.
\INS{ADDIU} следует за \INS{JALR} (помните о \IT{branch delay slots}?).
Регистр \$4 также называется \$A0, который используется для передачи первого аргумента функции
\footnote{Таблица регистров в MIPS доступна в приложении \myref{MIPS_registers_ref}}.
\myindex{MIPS!\Instructions!JALR}
\INS{JALR} (\q{Jump and Link Register}) делает переход по адресу в регистре \$25 (там адрес \puts) 
при этом сохраняя адрес следующей инструкции (\INS{LW}) в \ac{RA}.
Это так же как и в ARM.
И ещё одна важная вещь: адрес сохраняемый в \ac{RA} это адрес не следующей инструкции (потому что
это \IT{delay slot} и исполняется перед инструкцией перехода),
а инструкции после неё (после \IT{delay slot}).
Таким образом во время исполнения \INS{JALR} в \ac{RA} записывается $PC + 8$.
В нашем случае это адрес инструкции \INS{LW} следующей после \INS{ADDIU}.

\INS{LW} (\q{Load Word}) в строке 20 восстанавливает \ac{RA} из локального стека (эта инструкция скорее часть эпилога функции).

\myindex{MIPS!\Pseudoinstructions!MOVE}
\INS{MOVE} в строке 22 копирует значение из регистра \$0 (\$ZERO) в \$2 (\$V0).

\label{MIPS_zero_register}
В MIPS есть \IT{константный} регистр, всегда содержащий ноль.
Должно быть, разработчики MIPS решили, что 0 это самая востребованная константа в программировании,
так что пусть будет использоваться регистр \$0, всякий раз, когда будет нужен 0.
Другой интересный факт: в MIPS нет инструкции, копирующей значения из регистра в регистр.
На самом деле, \TT{MOVE DST, SRC} это \TT{ADD DST, SRC, \$ZERO} ($DST=SRC+0$), которая делает тоже самое.
Очевидно, разработчики MIPS хотели сделать как можно более компактную таблицу опкодов.
Это не значит, что сложение происходит во время каждой инструкции \INS{MOVE}.
Скорее всего, эти псевдоинструкции оптимизируются в \ac{CPU} и \ac{ALU} никогда не используется.

\myindex{MIPS!\Instructions!J}
\INS{J} в строке 24 делает переход по адресу в \ac{RA}, и это работает как выход из функции.
\INS{ADDIU} после \INS{J} на самом деле исполняется перед \INS{J} (помните о \IT{branch delay slots}?) 
и это часть эпилога функции.

Вот листинг сгенерированный \IDA. Каждый регистр имеет свой псевдоним:

\lstinputlisting[caption=\Optimizing GCC 4.4.5 (\IDA),numbers=left,style=customasmMIPS]{patterns/01_helloworld/MIPS/hw_O3_IDA_RU.lst}

Инструкция в строке 15 сохраняет GP в локальном стеке. Эта инструкция мистическим образом отсутствует
в листинге от GCC, может быть из-за ошибки в самом GCC\footnote{Очевидно, функция вывода листингов не так критична
для пользователей GCC, поэтому там вполне могут быть неисправленные ошибки.}.
Значение GP должно быть сохранено, потому что всякая функция может работать со своим собственным окном данных
размером 64KiB.
Регистр, содержащий адрес функции \puts называется \$T9, потому что регистры с префиксом T- называются
\q{temporaries} и их содержимое можно не сохранять.

\subsubsection{\NonOptimizing GCC}

\NonOptimizing GCC более многословный.

\lstinputlisting[caption=\NonOptimizing GCC 4.4.5 (\assemblyOutput),numbers=left,style=customasmMIPS]{patterns/01_helloworld/MIPS/hw_O0_RU.s}

Мы видим, что регистр FP используется как указатель на фрейм стека.
Мы также видим 3 \ac{NOP}-а.
Второй и третий следуют за инструкциями перехода.
Видимо, компилятор GCC всегда добавляет \ac{NOP}-ы (из-за \IT{branch delay slots})
после инструкций переходов и затем, если включена оптимизация, от них может избавляться.
Так что они остались здесь.

Вот также листинг от \IDA:

\lstinputlisting[caption=\NonOptimizing GCC 4.4.5 (\IDA),numbers=left,style=customasmMIPS]{patterns/01_helloworld/MIPS/hw_O0_IDA_RU.lst}

\myindex{MIPS!\Pseudoinstructions!LA}
Интересно что \IDA распознала пару инструкций \INS{LUI}/\INS{ADDIU} и собрала их в одну псевдоинструкцию 
\INS{LA} (\q{Load Address}) в строке 15.
Мы также видим, что размер этой псевдоинструкции 8 байт!
Это псевдоинструкция (или \IT{макрос}), потому что это не настоящая инструкция MIPS, а скорее
просто удобное имя для пары инструкций.

\myindex{MIPS!\Pseudoinstructions!NOP}
\myindex{MIPS!\Instructions!OR}
Ещё кое что: \IDA не распознала \ac{NOP}-инструкции в строках 22, 26 и 41.

Это \TT{OR \$AT, \$ZERO}.
По своей сути это инструкция, применяющая операцию \IT{ИЛИ} к содержимому регистра \$AT с нулем,
что, конечно же, холостая операция.
MIPS, как и многие другие \ac{ISA}, не имеет отдельной \ac{NOP}-инструкции.

\subsubsection{Роль стекового фрейма в этом примере}

Адрес текстовой строки передается в регистре.
Так зачем устанавливать локальный стек?
Причина в том, что значения регистров \ac{RA} и GP должны быть сохранены где-то
(потому что вызывается \printf) и для этого используется локальный стек.

Если бы это была \gls{leaf function}, тогда можно было бы избавиться от пролога и эпилога функции. Например:
 \myref{MIPS_leaf_function_ex1}.

\subsubsection{\Optimizing GCC: загрузим в GDB}

\myindex{GDB}
\lstinputlisting[caption=пример сессии в GDB]{patterns/01_helloworld/MIPS/O3_GDB.txt}

